﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GameEngine.MoveHistory
{
    /// <summary>
    /// New history model keeping track of branched variations all elements of PDN 3.0 standard
    /// </summary>
    internal class TreeMoveHistoryModel: IMoveHistoryModel
    {
        /// <summary>
        /// Game Tree Root object
        /// </summary>
        private GameTree _gameRoot = null;

        /// <summary>
        /// The current move done of FEN to set up
        /// </summary>
        private GameTree _currentNode = null;

        /// <summary>
        /// Reference to the game board
        /// </summary>
        public GameBoard GameBoard
        { get; set; }

        public TreeMoveHistoryModel()
        {
            _gameRoot = new GameTree(null);
        }

        /// <summary>
        /// Obsolete
        /// </summary>
        public void GetMoveList(List<Move> list)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Obsolete
        /// </summary>
        public void GetMovesDone(List<Move> list)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Obsolete
        /// </summary>
        public void GetMovesToDo(List<Move> list)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Clears the game record
        /// </summary>
        public void ClearMoves()
        {
            _gameRoot.ClearItems();
            _currentNode = null;
        }

        /// <summary>
        /// Obsolete
        /// </summary>
        public void ClearMovesToDo()
        {
            throw new NotImplementedException();
        }

        public void AddMove(Move move)
        {
            // new parameter newVaration: bool
            // create treeNode
            // if curNode null => add it to the root
            // else
            //   if(newVariation) => 
            //              create new gameBodyItem and its treeNode, 
            //              insertAfter parent treeNode - new method of treeNode
            //              insert the the move treeNode into the branch
            //   else => deleteContent after currentIndex (method in the GameTree)
            //                                append new treeNode             
            // set curnode to entered treeNode

            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the count of moves done
        /// </summary>
        public int GetMovesDoneCount()
        {
            int count = 0;
            GameTree item = _currentNode;
            do
            {
                item = GetPreviousMoveNode(item);
                count++;
            }
            while (item != null);

            return count;
        }

        public int GetMoveIndex()
        {
            return _currentNode == null ? -1 : _currentNode.Index;
        }

        /// <summary>
        /// Moves the game match to given index even finding the correct varation the final move might be in
        /// </summary>
        public void MoveToIndex(int idx)
        {
            // initialization
            bool forward = true;
            GameTree finalItem = FindNodeByIndex(idx);
            int curIndex = _currentNode.Index;
            if (idx < curIndex)
                forward = false;
            
            // switch between two move directions
            if (forward)
            {
                List<GameTree> path = LookUpPath(_currentNode, finalItem);

                foreach (var item in path)
                {
                    GameBoard.DoMove(item.GameItem as Move);
                    GameBoard.ChangeOnMove();
                }
            }
            else
            {
                List<GameTree> path = LookUpPath(finalItem, _currentNode);

                foreach (var item in path)
                {
                    GameBoard.UndoMove(item.GameItem as Move);
                    GameBoard.ChangeOnMove();
                }
            }

            _currentNode = finalItem;
        }

        /// <summary>
        /// Checks, whether can move forward
        /// </summary>
        /// <returns></returns>
        public bool CanMoveForward()
        {
            return GetPreviousMoveNode(_currentNode) != null;
        }

        /// <summary>
        /// Checks, whether can move back
        /// </summary>
        /// <returns></returns>
        public bool CanMoveBack()
        {
            return GetNextMoveNode() != null;
        }

        /// <summary>
        /// Does the move back
        /// </summary>
        public void MoveBack()
        {
            if (CanMoveBack() == false)
                return;
            GameTree prevNode = GetPreviousMoveNode(_currentNode);
            if (prevNode.GameItem is Move)
            {
                GameBoard.UndoMove(prevNode.GameItem as Move);
                GameBoard.ChangeOnMove();
            }
            // TO-DO: add support for FEN Setup
            _currentNode = prevNode;
        }

        /// <summary>
        /// Does move forward
        /// </summary>
        public void MoveForward()
        {
            if (CanMoveBack() == false)
                return;
            GameTree nextNode = GetNextMoveNode();
            if (nextNode.GameItem is Move)
            {
                GameBoard.DoMove(nextNode.GameItem as Move);
                GameBoard.ChangeOnMove();
            }
            // TO-DO: add support for FEN Setup
            _currentNode = nextNode;
        }


        #region Private Methods

        /// <summary>
        /// checkes whether node updates the gameboard
        /// </summary>
        private bool IsMoveNode(GameTree treeNode)
        {
            // add support for FEN setup
            return treeNode.GetNodeType() == eGameType.Move;            
        }
        
        /// <summary>
        /// Returns previous game board updating node
        /// </summary>
        /// <returns></returns>
        private GameTree GetPreviousMoveNode(GameTree item)
        {
            do
            {
                item = item.GetPreviousNodeUpToRoot();
                if (IsMoveNode(item))
                    return item;
            }
            while (item != null);

            return null;                  
        }

        /// <summary>
        /// Returns the next updating node
        /// </summary>
        private GameTree GetNextMoveNode()
        {
            GameTree item = _currentNode;
            do
            {
                item = item.GetNextSibling();
                if (IsMoveNode(item))
                    return item;
            }
            while (item != null);

            return null;                  
        }

        /// <summary>
        /// Traverses the tree to look up a node in the game tree by its index
        /// </summary>
        private GameTree FindNodeByIndex(int index, GameTree parent = null)
        {
            if (parent == null)
                parent = _gameRoot;
            foreach (var childNode in parent.ChildNodes)
            {
                if (childNode.Index == index)
                    return childNode;
                if (childNode.GetNodeType() == eGameType.Variation)
                {
                    var subChildNode = FindNodeByIndex(index, childNode);
                    if (subChildNode != null)
                        return subChildNode;
                }
            }

            return null;
        }

        /// <summary>
        /// Looks up the move-path between two nodes
        /// </summary>
        private List<GameTree> LookUpPath(GameTree first, GameTree second)
        {
            List<GameTree> path = new List<GameTree>();

            GameTree item = second;
            while (item != first)
            {
                path.Insert(0, item);
                item = GetPreviousMoveNode(item);
            }

            return path;
        }

        #endregion
    }
}
